package com.smarthome.ipc.hk.entity;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ResourceBundle;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.smarthome.domain.ipc.IpcCodeEnum;
import com.smarthome.domain.ipc.IpcConstants;
import com.smarthome.domain.ipc.req.HkIpcResidenceTalkDoorRequest;
import com.smarthome.domain.ipc.req.HkIpcResidenceVideoDoorRequest;
import com.smarthome.domain.ipc.req.IpcLogRecordRequest;
import com.smarthome.ipc.IpcClientMain;
import com.smarthome.ipc.hk.demo.HCNetSDK;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_CHANNELSTATE_V30;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_CLIENTINFO;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_COMPRESSION_AUDIO;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_DEVICEINFO_V30;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_IPPARACFG;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_VIDEO_CALL_COND;
import com.smarthome.ipc.hk.demo.HCNetSDK.NET_DVR_WORKSTATE_V30;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.IntByReference;

/**
 * 设备的登录实体。每个门口机对应一个登录实体。
 * 
 * @author denghw
 *
 */
public class NetDvrLoginV30Entity {
	private static final Logger LOGGER = LoggerFactory.getLogger(NetDvrLoginV30Entity.class);
	private final static String PRE_LOGGER = "[NetDvrLoginV30Entity]";
	private static ResourceBundle resource = ResourceBundle.getBundle("conf/ipc-config");

	// -1表示失败，其他值表示返回的用户ID值。该用户ID具有唯一性，后续对设备的操作都需要通过此ID实现。
	private NativeLong iUserId;
	// 设备参数
	private NET_DVR_DEVICEINFO_V30 deviceInfo;
	// 唯一编码
	private String unicode;
	// 门口机SDK操作句柄
	private HCNetSDK hCNetSDK;

	// 设备Id
	private String deviceid;

	// 可视对讲参数-开始
	private NET_DVR_WORKSTATE_V30 m_strWorkState;// 工作状态
	private boolean m_bInitialed;// 是否已初始化
	private FileWriter fLocal;// 本地音频文件
	private FileWriter fDevice;// 设备发送的音频文件
	private NativeLong g_lVoiceHandle;// 语音对讲句柄
	private NET_DVR_COMPRESSION_AUDIO audiocfgtype;
	private byte audiochannel[] = null;// 音频通道数组,值为通道号
	private byte audiochanneltype[] = null;// 值表示编码格式
	private int audiochannelnum = 0; // 音频通道个数
	private int m_iSel;// 音频通道号
	private int m_iSelindex;// 音频通道号对应数组下标
	// 可视对讲参数-结束
	// 视频预览参数-开始
	private NativeLong g_lvideoHandle_review;// 预览视频句柄
	private byte videochannel[] = null;// 视频通道数组,值为通道号
	private int videochannelnum = 0; // 视频通道个数
	private int video_iSel_review;// 当前视频通道号
	private NET_DVR_IPPARACFG videocfgtyep;
	private NET_DVR_CLIENTINFO m_strClientInfo;// 用户参数
	// 视频预览参数-结束

	// 初始化语言转发
	public void initVoiceTalk() {
		if (!m_bInitialed) {
			try {
				m_iSel = 0;
				fLocal = new FileWriter(resource.getString("ipc.hk.talk.path") + unicode + "local.264");
				fDevice = new FileWriter(resource.getString("ipc.hk.talk.path") + unicode + "device.264");
				m_bInitialed = true;
			} catch (IOException ex) {
				m_bInitialed = false;
			}
		}
	}

	// 初始函数,功能:设置音频格式
	public void init() {

		// 1.获取设备状态
		m_strWorkState = new HCNetSDK.NET_DVR_WORKSTATE_V30();
		
		if (!hCNetSDK.NET_DVR_GetDVRWorkState_V30(iUserId, m_strWorkState)) {
			LOGGER.info(PRE_LOGGER + "海康门口机SDK启动初始化失败["+hCNetSDK.NET_DVR_GetLastError()+"]");
			rtnBackErrMsg(deviceid, "NET_DVR_GetDVRWorkState_V30", String.valueOf(hCNetSDK.NET_DVR_GetLastError()),
					"海康门口机SDK启动初始化失败");
			return;
		}
		// 2.获取设备音频通道编码格式
		byte audionum = deviceInfo.byAudioChanNum;// 语言通道数
		byte audiostartch = deviceInfo.byStartChan;// 开始通道数
		audiochannel = new byte[audionum];
		audiochanneltype = new byte[audionum];
		audiocfgtype = new HCNetSDK.NET_DVR_COMPRESSION_AUDIO();
		int tempcc = 0;
		for (int i = 0; i < audionum; i++) {
			if (0 == m_strWorkState.byAudioChanStatus[audiostartch + i]) {
				// 3.设置音频格式
				audiocfgtype.byAudioEncType = IpcConstants.IPC_COMMAND_RESIDENCE_DOOR_TALK_HK_IS_G722;// 默认设置音频编码格式为G722
				Pointer audiocfgtypepointer = audiocfgtype.getPointer();
				boolean issuc = hCNetSDK.NET_DVR_SetDVRConfig(iUserId, HCNetSDK.NET_DVR_SET_COMPRESSCFG_AUD,
						new NativeLong(audiostartch + i), audiocfgtypepointer, audiocfgtype.size());
				if (issuc) {
					audiochannel[tempcc] = (byte) (audiostartch + i);
					audiochanneltype[tempcc] = audiocfgtype.byAudioEncType;
					tempcc++;
				}
			}
		}
		audiochannelnum = tempcc;
		// 3.获取视频通道
		IntByReference ibrBytesReturned = new IntByReference(0);// 获取IP接入配置参数
		boolean bRet = false;
		videocfgtyep = new HCNetSDK.NET_DVR_IPPARACFG();
		videocfgtyep.write();
		Pointer lpIpParaConfig = videocfgtyep.getPointer();
		bRet = hCNetSDK.NET_DVR_GetDVRConfig(iUserId, HCNetSDK.NET_DVR_GET_IPPARACFG, new NativeLong(0), lpIpParaConfig,
				videocfgtyep.size(), ibrBytesReturned);
		videocfgtyep.read();
		if (!bRet) {
			videochannel = new byte[deviceInfo.byChanNum];
			int tmpc = 0;
			// 设备不支持,则表示没有IP通道
			for (int iChannum = 0; iChannum < deviceInfo.byChanNum; iChannum++) {
				if (videocfgtyep.byAnalogChanEnable[iChannum] == 1) {
					videochannel[tmpc] = (byte) (deviceInfo.byStartChan + iChannum);// 通道号
					tmpc++;
				}
			}
			videochannelnum = tmpc;// 记录通道个数

		} else {
			videochannel = new byte[deviceInfo.byChanNum + HCNetSDK.MAX_IP_CHANNEL];
			int tmpc = 0;
			// 设备支持IP通道
			for (int iChannum = 0; iChannum < deviceInfo.byChanNum; iChannum++) {
				if (videocfgtyep.byAnalogChanEnable[iChannum] == 1) {
					videochannel[tmpc] = (byte) (deviceInfo.byStartChan + iChannum);// 通道号
					tmpc++;
				}
			}
			for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
				if (videocfgtyep.struIPChanInfo[iChannum].byEnable == 1) {
					videochannel[tmpc] = (byte) (deviceInfo.byStartChan + HCNetSDK.MAX_ANALOG_CHANNUM + iChannum);// 通道号
					tmpc++;
				}
			}
			videochannelnum = tmpc;// 记录通道个数
		}

		// 4. 启动对讲功能
		NET_DVR_VIDEO_CALL_COND videoCallCond = new HCNetSDK.NET_DVR_VIDEO_CALL_COND();
		videoCallCond.write();
		videoCallCond.dwSize = videoCallCond.size();
		videoCallCond.write();
		Pointer videoCallCondpointer = videoCallCond.getPointer();
		hCNetSDK.NET_DVR_StartRemoteConfig(iUserId, HCNetSDK.NET_DVR_VIDEO_CALL_SIGNAL_PROCESS, videoCallCondpointer,
				videoCallCond.size(), new FRemoteConfigCallback(), null);
		videoCallCond.read();
	}

	// 语言转发开始
	public boolean startVoiceTalk(HkIpcResidenceTalkDoorRequest sprequest) {
		// 1.判断当前是否已经正在进行对讲
		if (g_lVoiceHandle.intValue() >= 0)
			return true;
		// 2.获取设备工作状态
		m_strWorkState = new HCNetSDK.NET_DVR_WORKSTATE_V30();
		if (!hCNetSDK.NET_DVR_GetDVRWorkState_V30(iUserId, m_strWorkState)) {
			return false;
		}
		// 3.获取可用的语音通道号(注:这里获取门口机自身摄像头数据,厂商给的测试开发机只有一个模拟语言通道。最后上线代码这块需要根据实际的情况进行修正。)
		int findchannelindex = -1;
		for (int i = 0; i < audiochannelnum; i++) {
			if (0 == m_strWorkState.byAudioChanStatus[audiochannel[i]]) {
				m_iSel = audiochannel[i];
				findchannelindex = i;
				break;
			}
		}
		if (findchannelindex == -1)
			return false;
		// 4.设置音频格式
		m_iSelindex = findchannelindex;
		if (audiochanneltype[m_iSelindex] != sprequest.getEncodeType()) {
			audiocfgtype.byAudioEncType = sprequest.getEncodeType();
			Pointer audiocfgtypepointer = audiocfgtype.getPointer();
			boolean issuc = hCNetSDK.NET_DVR_SetDVRConfig(iUserId, HCNetSDK.NET_DVR_SET_COMPRESSCFG_AUD,
					new NativeLong(m_iSel), audiocfgtypepointer, audiocfgtype.size());
			if (!issuc)
				return false;
			audiochanneltype[m_iSelindex] = sprequest.getEncodeType();// 更新内存变量
		}
		// 5.和设备建立语言通道连接
		g_lVoiceHandle = hCNetSDK.NET_DVR_StartVoiceCom_MR_V30(iUserId, m_iSel, new FVoiceDataCallBack(), null);
		if (g_lVoiceHandle.intValue() == -1)
			return false;
		// 6.返回成功
		return true;
	}

	// 获取语音码流并推送给设备,注：这里直接将码流推送到设备上,需要在调用前根据音频格式进行编码操作.
	public void receiveVoiceBytes(byte voiceType, String pRecvDataBuffer) {

		// 1.获取设备的音频编码类型
		byte tmpvoit = audiochannel[m_iSelindex];
		if (tmpvoit != voiceType) {// 若编码与内存变量不一致,为了保险起见,直接从设备中获取一次。
			IntByReference ibrBytesReturned = new IntByReference(0);
			boolean bRet = false;
			audiocfgtype = new HCNetSDK.NET_DVR_COMPRESSION_AUDIO();
			audiocfgtype.write();
			Pointer audiocfgtypepointer = audiocfgtype.getPointer();
			bRet = hCNetSDK.NET_DVR_GetDVRConfig(iUserId, HCNetSDK.NET_DVR_GET_COMPRESSCFG_AUD, new NativeLong(m_iSel),
					audiocfgtypepointer, audiocfgtype.size(), ibrBytesReturned);
			audiocfgtype.read();
			if (!bRet) {
				LOGGER.info(PRE_LOGGER + "海康门口机SDK获取到设备语音编码类型失败");
				rtnBackErrMsg(deviceid, "NET_DVR_GetDVRConfig", String.valueOf(hCNetSDK.NET_DVR_GetLastError()),
						"海康门口机SDK获取到设备语音编码类型失败");
				return;
			}
			audiochanneltype[m_iSelindex] = audiocfgtype.byAudioEncType;// 更新内存变量
			tmpvoit = audiochanneltype[m_iSelindex];
		}

		// 2.设置缓冲区大小
		int bufsize = 80;
		if (tmpvoit == voiceType && IpcConstants.IPC_COMMAND_RESIDENCE_DOOR_TALK_HK_IS_G722 == voiceType) {
			bufsize = 80;
		} else if (tmpvoit == voiceType && IpcConstants.IPC_COMMAND_RESIDENCE_DOOR_TALK_HK_IS_G711_U == voiceType) {
			bufsize = 160;
		} else if (tmpvoit == voiceType && IpcConstants.IPC_COMMAND_RESIDENCE_DOOR_TALK_HK_IS_G711_A == voiceType) {
			bufsize = 160;
		} else {
			LOGGER.info(PRE_LOGGER + "海康门口机SDK获取到的音频格式类型识别不到,注:支持G722和G711");
			rtnBackErrMsg(deviceid, "receiveVoiceBytes", "not found voiceType", "海康门口机SDK获取到的音频格式类型识别不到,注:支持G722和G711");
			return;
		}
		// 3.推送音频数据到设备
		hCNetSDK.NET_DVR_VoiceComSendData(g_lVoiceHandle, pRecvDataBuffer, bufsize);
	}

	// 语言转发结束
	public boolean stopVoiceTalk() {
		if (g_lVoiceHandle.intValue() >= 0) {
			if (hCNetSDK.NET_DVR_StopVoiceCom(g_lVoiceHandle)) {
				g_lVoiceHandle.setValue(-1);
			} else {
				return false;
			}
		}
		m_iSel = 0;
		m_bInitialed = false;
		return true;
	}

	// 视频预览开始
	public boolean startRealPlay(HkIpcResidenceVideoDoorRequest sprequest) {
		// 1.判断当前是否已经正在进行视频预览
		if (g_lvideoHandle_review.intValue() >= 0)
			return true;
		// 2.获取设备工作状态
		m_strWorkState = new HCNetSDK.NET_DVR_WORKSTATE_V30();
		if (!hCNetSDK.NET_DVR_GetDVRWorkState_V30(iUserId, m_strWorkState)) {
			return false;
		}
		// 3.获取可用的视频通道号(注:这里获取门口机自身摄像头数据,厂商给的测试开发机只有一个模拟语言通道。最后上线代码这块需要根据实际的情况进行修正。)
		int findchannelindex = -1;
		for (int i = 0; i < videochannelnum; i++) {
			NET_DVR_CHANNELSTATE_V30 videoobj = m_strWorkState.struChanStatic[videochannel[i]];
			if (videoobj.bySignalStatic == 0 && videoobj.byHardwareStatic == 0) {
				video_iSel_review = videochannel[i];
				findchannelindex = i;
				break;
			}
		}
		if (findchannelindex == -1)
			return false;
		// 4.和设备建立语言通道连接
		m_strClientInfo = new HCNetSDK.NET_DVR_CLIENTINFO();
		m_strClientInfo.lChannel = new NativeLong(video_iSel_review);
		m_strClientInfo.hPlayWnd = null;
		g_lvideoHandle_review = hCNetSDK.NET_DVR_RealPlay_V30(iUserId, m_strClientInfo, new FRealDataCallBack(), null,
				true);
		if (g_lvideoHandle_review.intValue() == -1)
			return false;
		// 6.返回成功
		return true;
	}

	// 视频预览结束
	public boolean stopRealPlay() {
		if (g_lvideoHandle_review.intValue() >= 0) {
			if (hCNetSDK.NET_DVR_StopRealPlay(g_lvideoHandle_review)) {
				g_lvideoHandle_review.setValue(-1);
			} else {
				return false;
			}
		}
		video_iSel_review = 0;
		return true;
	}

	public NativeLong getiUserId() {
		return iUserId;
	}

	public void setiUserId(NativeLong iUserId) {
		this.iUserId = iUserId;
	}

	public HCNetSDK gethCNetSDK() {
		return hCNetSDK;
	}

	public void sethCNetSDK(HCNetSDK hCNetSDK) {
		this.hCNetSDK = hCNetSDK;
	}

	public void setDeviceInfo(NET_DVR_DEVICEINFO_V30 deviceInfo) {
		this.deviceInfo = deviceInfo;
	}

	public void setUnicode(String unicode) {
		this.unicode = unicode;
	}

	public void setDeviceid(String deviceid) {
		this.deviceid = deviceid;
	}

	public HCNetSDK.NET_DVR_WORKSTATE_V30 getM_strWorkState() {
		return m_strWorkState;
	}

	/******************************************************************************
	 * 内部类: FVoiceDataCallBack 实现音频转发数据回调。
	 ******************************************************************************/
	class FVoiceDataCallBack implements HCNetSDK.FVoiceDataCallBack_MR_V30 {
		@Override
		public void invoke(NativeLong lVoiceComHandle, String pRecvDataBuffer, int dwBufSize, byte byAudioFlag,
				String pUser) {
			// TODO Auto-generated method stub
			// byAudioFlag为0表示本地文件,为1表示设备的音频文件
			if (byAudioFlag == 0) {// 语言转发,这个方法不会执行。
				try {
					fLocal.write(pRecvDataBuffer);
					fLocal.flush();
				} catch (IOException ex) {
					LOGGER.info(PRE_LOGGER + "海康门口机回调函数【语言转发回调本地音频】执行产生异常后，保存音频文件产生异常！" + ex.getMessage());
					rtnBackErrMsg(deviceid, "fLocal", ex.getMessage(), "输出本地音频文件失败");
				}
			} else {
				if (byAudioFlag == 1) {
					try {
						fDevice.write(pRecvDataBuffer);
						fDevice.flush();
						// 向通道发送码流.
					} catch (IOException ex) {
						LOGGER.info(PRE_LOGGER + "海康门口机回调函数【语言转发回调设备音频】执行产生异常后，向云平台推送语言码流产生异常！" + ex.getMessage());
						rtnBackErrMsg(deviceid, "fDevice", ex.getMessage(), "输出失败");
					}

				}
			}

		}

	}

	// 视频预览回调函数
	class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {

		@Override
		public void invoke(NativeLong lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize,
				Pointer pUser) {
			// TODO Auto-generated method stub
			// 该处待后续通道建立OK之后写代码

		}

	}

	/******************************************************************************
	 * 内部类: FRemoteConfigCallback 远程配置回调函数(如对讲功能)
	 ******************************************************************************/
	class FRemoteConfigCallback implements HCNetSDK.FRemoteConfigCallback {

		@Override
		public void invoke(int dwType, int lpBuffer, int dwBufLen, int pUserData) {
			// TODO Auto-generated method stub
			switch (HCNetSDK.NET_SDK_CALLBACK_TYPE.values()[dwType]) {
			case NET_SDK_CALLBACK_TYPE_STATUS: {

			}
			case NET_SDK_CALLBACK_TYPE_PROGRESS: {

			}
			case NET_SDK_CALLBACK_TYPE_DATA: {
				// Pointer videoCallParampointer =
				// Pointer.createConstant(lpBuffer);
				// NET_DVR_VIDEO_CALL_PARAM videoCallParam = new
				// HCNetSDK.NET_DVR_VIDEO_CALL_PARAM();
				// videoCallParam.dwSize = dwBufLen;
				//
				// videoCallParam.read();
				// if(videoCallParam.dwCmdType==0){//接收门口机呼叫
				System.out.println("----------------门口机呼叫-------------");

			}

			}

		}

	}

	private void rtnBackErrMsg(String sn, String cmd, String errCode, String errMsg) {
		IpcLogRecordRequest requestrtn = new IpcLogRecordRequest();
		requestrtn.setCommand(IpcConstants.IPC_COMMAND_LOG_RECORD);
		requestrtn.setCmd(cmd);
		requestrtn.setContent("海康门口机回调函数执行产生异常后,错误代码为[" + errCode + "],错误信息为[" + errMsg + "].");
		requestrtn.setRemark("");
		requestrtn.setFailTimes(0);
		requestrtn.setSn(sn);
		requestrtn.setRtnCode(IpcCodeEnum.SYSTEM_ERROR.getCode());
		try {
			IpcClientMain.getInstance().sendCommand(requestrtn);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			LOGGER.info(PRE_LOGGER + "海康门口机回调函数执行产生异常后，向云平台推送错误日志产生异常！" + e1.getMessage());
		}

	}
}
